package com.admin.system.modules.quartz.rest;

import com.admin.common.constant.ExceptionConstant;
import com.admin.common.exception.BadRequestException;
import com.admin.common.utils.PageResult;
import com.admin.common.utils.SpringContextHolder;
import com.admin.component.web.MultipartFileUtil;
import com.admin.logging.annotation.Log;
import com.admin.logging.enums.OperateTypeEnum;
import com.admin.system.modules.quartz.QuartzManage;
import com.admin.system.modules.quartz.entity.QuartzJob;
import com.admin.system.modules.quartz.entity.QuartzLog;
import com.admin.system.modules.quartz.entity.dto.QuartzJobQueryDTO;
import com.admin.system.modules.quartz.service.QuartzJobService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author sq-lan
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/jobs")
@Api(tags = "系统:定时任务管理")
public class QuartzJobController {

    private final QuartzJobService quartzJobService;
    private final QuartzManage quartzManage;

    @ApiOperation("查询定时任务")
    @GetMapping
    @PreAuthorize("@ss.check('timing:list')")
    public ResponseEntity<PageResult<QuartzJob>> paging(QuartzJobQueryDTO dto) {
        return new ResponseEntity<>(quartzJobService.paging(dto), HttpStatus.OK);
    }

    @ApiOperation("查询任务执行日志")
    @GetMapping(value = "/logs")
    @PreAuthorize("@ss.check('timing:list')")
    public ResponseEntity<PageResult<QuartzLog>> pagingLog(QuartzJobQueryDTO dto) {
        return new ResponseEntity<>(quartzJobService.pagingLog(dto), HttpStatus.OK);
    }

    @Log(module = "定时任务管理", operateType = OperateTypeEnum.ADD)
    @ApiOperation("新增定时任务")
    @PostMapping
    @PreAuthorize("@ss.check('timing:add')")
    public ResponseEntity<Object> add(@Validated @RequestBody QuartzJob entity) {
        if (entity.getId() != null) {
            throw new BadRequestException(ExceptionConstant.ERROR_PARAM);
        }
        checkBean(entity.getBeanName());
        quartzJobService.add(entity);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }

    @Log(module = "定时任务管理", operateType = OperateTypeEnum.MODIFY)
    @ApiOperation("修改定时任务")
    @PutMapping
    @PreAuthorize("@ss.check('timing:edit')")
    public ResponseEntity<Object> modify(@Validated(QuartzJob.Update.class) @RequestBody QuartzJob entity) {
        checkBean(entity.getBeanName());
        quartzJobService.modify(entity);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log(module = "定时任务管理", operateType = OperateTypeEnum.MODIFY)
    @ApiOperation("更改定时任务状态")
    @PutMapping(value = "/{id}")
    @PreAuthorize("@ss.check('timing:edit')")
    public ResponseEntity<Object> updateStatus(@PathVariable Integer id) {
        quartzJobService.updateIsPause(quartzJobService.getById(id));
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @Log(module = "定时任务管理", operateType = OperateTypeEnum.REMOVE)
    @ApiOperation("删除定时任务")
    @DeleteMapping
    @PreAuthorize("@ss.check('timing:del')")
    public ResponseEntity<Object> remove(@RequestBody Set<Integer> ids) {
        quartzJobService.remove(ids);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Log(module = "定时任务管理", description = "执行定时任务")
    @ApiOperation("执行定时任务")
    @PutMapping(value = "/exec/{id}")
    @PreAuthorize("@ss.check('timing:edit')")
    public ResponseEntity<Object> execution(@PathVariable Integer id) {
        quartzManage.execution(quartzJobService.getById(id));
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    @ApiOperation("导出任务数据")
    @GetMapping(value = "/download")
    @PreAuthorize("@ss.check('timing:list')")
    public void export(HttpServletResponse response, QuartzJobQueryDTO dto) throws IOException {
        List<QuartzJob> list = quartzJobService.list(dto);
        List<Map<String, Object>> result = new ArrayList<>();
        for (QuartzJob quartzJob : list) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("任务名称", quartzJob.getJobName());
            map.put("Bean名称", quartzJob.getBeanName());
            map.put("执行方法", quartzJob.getMethodName());
            map.put("表达式", quartzJob.getCronExpression());
            map.put("参数", quartzJob.getParams());
            map.put("状态", quartzJob.getIsPause() ? "暂停中" : "运行中");
            map.put("描述", quartzJob.getDescription());
            map.put("创建日期", quartzJob.getCreateTime());
            result.add(map);
        }
        MultipartFileUtil.downloadExcel(result, response);
    }

    @ApiOperation("导出日志数据")
    @GetMapping(value = "/logs/download")
    @PreAuthorize("@ss.check('timing:list')")
    public void exportLog(HttpServletResponse response, QuartzJobQueryDTO dto) throws IOException {
        List<QuartzLog> list = quartzJobService.listLog(dto);
        List<Map<String, Object>> result = new ArrayList<>();
        for (QuartzLog quartzLog : list) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("任务名称", quartzLog.getJobName());
            map.put("Bean名称", quartzLog.getBeanName());
            map.put("执行方法", quartzLog.getMethodName());
            map.put("参数", quartzLog.getParams());
            map.put("表达式", quartzLog.getCronExpression());
            map.put("异常详情", quartzLog.getExceptionDetail());
            map.put("耗时/毫秒", quartzLog.getTime());
            map.put("状态", quartzLog.getIsSuccess() ? "成功" : "失败");
            map.put("创建日期", quartzLog.getCreateTime());
            result.add(map);
        }
        MultipartFileUtil.downloadExcel(result, response);
    }

    private void checkBean(String beanName) {
        // 避免调用攻击者可以从SpringContextHolder获得控制jdbcTemplate类
        // 并使用getDeclaredMethod调用jdbcTemplate的queryForMap函数，执行任意sql命令。
        if (!SpringContextHolder.listAllServiceBeanName().contains(beanName)) {
            throw new BadRequestException("非法的 Bean.");
        }
    }

}
